package lion.dev.image;

import java.awt.Color;
import java.awt.Dimension;
import java.awt.image.BufferedImage;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;

import lion.dev.image.gif.AnimatedGifEncoder;
import lion.dev.image.gif.GifDecoder;

/**
 * GIF 格式图片处理，将图片的每一祯图片取出来分别处理，然后再合成输出
 *
 * - 如果图片只有一祯，使用ImageIO输出图，这样可以避免encoder带来的图片质量损失
 * - 如果图片有多祯，使用{@code AnimatedGifEncoder} 合成输出
 *
 * @author helin
 * @email hl_0074@sina.com
 * @date 2016年8月16日
 */
public class GifHandler implements IImageHandler {

	private GifPice[] frames;
	private int loopCount;
	private boolean hasTransparent = false;

	@Override
	public void read(InputStream src) throws IOException {

		GifDecoder decoder = new GifDecoder();
		decoder.read(src);
		loopCount = decoder.getLoopCount();

		frames = new GifPice[decoder.getFrameCount()];
		for (int i = 0; i < decoder.getFrameCount(); i++) {
			frames[i] = new GifPice(decoder.getFrame(i), decoder.getDelay(i));
		}
		hasTransparent = isTransparentImage(frames[0].getImage());
	}

	@Override
	public void read(BufferedImage image) {

		frames = new GifPice[] { new GifPice(image, 1) };
	}

	@Override
	public IImageHandler compress(float quality, String type) {

		GifPice[] pices = new GifPice[this.frames.length];

		for (int i = 0; i < pices.length; i++) {
			pices[i] = new GifPice(DefaultImageHandler.compress(this.frames[i].getImage(), quality, "png"),
					this.frames[i].getDelay());
		}

		GifHandler handler = this.copy(pices);
		return handler;
	}

	private GifHandler copy(GifPice[] pices) {

		GifHandler handler = new GifHandler();
		handler.frames = pices;
		handler.hasTransparent = this.hasTransparent;
		handler.loopCount = this.loopCount;

		return handler;
	}

	/**
	 * TODO 部分透明gif图片处理后会变成黑色背景,或出现黑背景闪动情况
	 */
	@Override
	public IImageHandler resize(int width, int height) {

		// resize 每一张
		GifPice[] pices = new GifPice[this.frames.length];

		for (int i = 0; i < pices.length; i++) {
			pices[i] = new GifPice(DefaultImageHandler.resize(this.frames[i].getImage(), width, height),
					this.frames[i].getDelay());
		}
		GifHandler handler = this.copy(pices);

		return handler;
	}

	@Override
	public IImageHandler resizeWidth(int width) {

		int height = (int) (this.frames[0].getImage().getHeight() * (width * 1.0 / this.frames[0].getImage().getWidth()));

		return this.resize(width, height);
	}

	@Override
	public IImageHandler resizeHeight(int height) {

		Dimension dimension = getDimension();
		int width = (int) (dimension.getWidth() * (height * 1.0 / dimension.getHeight()));
		return this.resize(width, height);
	}

	@Override
	public IImageHandler resizeScale(float percent) {

		Dimension dimension = getDimension();
		int width = (int) (dimension.getWidth() * percent);
		int height = (int) (dimension.getHeight() * percent);
		return this.resize(width, height);
	}

	@Override
	public IImageHandler rotate(Rotation rotation) {

		GifPice[] pices = new GifPice[this.frames.length];
		for (int i = 0; i < pices.length; i++) {

			pices[i] = new GifPice(DefaultImageHandler.rotate(this.frames[i].getImage(), rotation),
					this.frames[i].getDelay());
		}

		GifHandler handler = this.copy(pices);

		return handler;
	}

	@Override
	public IImageHandler crop(int x, int y, int width, int height) {

		GifPice[] pices = new GifPice[this.frames.length];
		for (int i = 0; i < pices.length; i++) {

			pices[i] = new GifPice(DefaultImageHandler.crop(this.frames[i].getImage(), x, y, width, height),
					this.frames[i].getDelay());
		}

		GifHandler handler = this.copy(pices);

		return handler;
	}

	@Override
	public IImageHandler watermark(BufferedImage watermark, Directoin directoin, float opacity) {

		GifPice[] pices = new GifPice[this.frames.length];
		for (int i = 0; i < pices.length; i++) {

			pices[i] = new GifPice(DefaultImageHandler.merge(this.frames[i].getImage(), watermark, directoin, opacity),
					this.frames[i].getDelay());
		}

		GifHandler handler = this.copy(pices);

		return handler;
	}

	@Override
	public IImageHandler watermark(BufferedImage watermark, int x, int y, float opacity) {

		GifPice[] pices = new GifPice[this.frames.length];
		for (int i = 0; i < pices.length; i++) {

			pices[i] = new GifPice(DefaultImageHandler.merge(this.frames[i].getImage(), watermark, x, y, opacity),
					this.frames[i].getDelay());
		}

		GifHandler handler = this.copy(pices);

		return handler;
	}

	@Override
	public IImageHandler limit(int maxwidth, int maxheight) {

		GifPice[] pices = new GifPice[this.frames.length];
		for (int i = 0; i < pices.length; i++) {

			pices[i] = new GifPice(DefaultImageHandler.limit(this.frames[i].getImage(), maxwidth, maxheight),
					this.frames[i].getDelay());
		}

		GifHandler handler = this.copy(pices);

		return handler;
	}

	@Override
	public IImageHandler sharpen(int times) {

		// resize 每一张
		GifPice[] pices = new GifPice[this.frames.length];

		for (int i = 0; i < pices.length; i++) {
			pices[i] = new GifPice(DefaultImageHandler.sharpen(this.frames[i].getImage(), times),
					this.frames[i].getDelay());
		}
		GifHandler handler = this.copy(pices);

		return handler;
	}

	private Dimension getDimension() {

		return new Dimension(this.frames[0].getImage().getWidth(), this.frames[0].getImage().getHeight());
	}

	@Override
	public void write(OutputStream os, String type) throws IOException {

		AnimatedGifEncoder encoder = new AnimatedGifEncoder();
		encoder.start(os);
		// 查找透明像素
		if (this.hasTransparent) {
			encoder.setTransparent(new Color(Color.OPAQUE));
			encoder.setDispose(2);
		}

		encoder.setRepeat(this.loopCount);
		Dimension dimension = getDimension();
		encoder.setSize((int) dimension.getWidth(), (int) dimension.getHeight());

		for (int i = 0; i < this.frames.length; i++) {
			encoder.setDelay(this.frames[i].getDelay());
			encoder.addFrame(this.frames[i].getImage());
		}
		encoder.finish();
	}

	private static boolean isTransparentImage(BufferedImage image) {

		int i = 0;
		for (int y = image.getMinY(); y < image.getHeight(); y++) {
			for (int x = image.getMinX(); x < image.getWidth(); x++) {
				int rgb = image.getRGB(x, y);
				if (rgb == 0 || rgb == 0xffffffff) {
					i++;
				}
			}
		}
		return i * 1d / (image.getWidth() * image.getHeight()) > 0.01;
	}

	private static class GifPice {

		private int delay;
		private BufferedImage image;

		public GifPice(BufferedImage image, int delay) {
			this.delay = delay;
			this.image = image;
		}

		public int getDelay() {

			return delay;
		}

		public BufferedImage getImage() {

			return image;
		}

	}

}
